<template>
  <!--#region 筛选配置-->
  <el-popover
    ref="filterPaneRef"
    :virtual-ref="virtualRef"
    :visible="filterPaneVisible"
    :show-arrow="false"
    placement="bottom-start"
    width="540px"
  >
    <div
      v-click-popover-outside:field-picker:compare-symbol-picker.exclude="
				onClickPopoverOutside
			"
      style="--el-fill-color-light: #eaebec"
    >
      <!--#region 标题、关闭按钮-->
      <div class="flex justify-between items-center mb-4">
        <span class="text-xl text-gray-700 font-semibold">筛选</span>
        <div
          class="flex justify-center items-center p-2 hover:bg-[#e3e5e5] rounded"
          @click="toggleFilterPaneVisible(false)"
        >
          <i class="i-lucide-x" />
        </div>
      </div>
      <!--#endregion-->
      <!--#region 多个筛选条件合并逻辑-->
      <div
        v-if="filterConfig.conditions.length > 1"
        class="flex items-center gap-x-4"
      >
        <span class="text-zinc-400">满足</span>
        <el-radio-group v-model="filterConfig.logicSymbol">
          <el-radio label="所有条件" value="AND" />
          <el-radio label="任一条件" value="OR" />
        </el-radio-group>
      </div>
      <!--#endregion-->
      <el-scrollbar :max-height="420" view-class="pr-4">
        <div
          v-show="filterConfig.conditions.length > 0"
          class="border rounded p-2 bg-white flex flex-col gap-2"
        >
          <div
            v-for="(condition, index) in filterConfig.conditions"
            :key="condition.field"
            class="rounded bg-[#f7f7f8] px-4 py-2"
          >
            <!--#region 筛选配置项折叠面板头部-->
            <div
              class="flex justify-between max-w-full"
              @click="
								activeCollapseItems = toggle(
									activeCollapseItems,
									condition.field
								)
							"
            >
              <div class="flex gap-x-4 items-center w-[337px]">
                <el-button
                  v-click-outside="onClickOutside"
                  text
                  @click.stop="onPickField($event, index)"
                >
                  <i :class="[typeIcon[condition.$type], 'text-[16px] mr-1']" />
                  <span>{{ condition.title }}</span>
                  <i class="i-lucide-chevron-down ml-1" />
                </el-button>
                <el-tooltip
                  v-if="!activeCollapseItems.includes(condition.field)"
                  :content="getCompareValue(condition)"
                  effect="light"
                  placement="top"
                  :disabled="!getCompareValue(condition)"
                  :show-arrow="false"
                  class="flex-1"
                >
                  <div class="text-zinc-400 truncate select-none">
                    {{ getCompareLabel(condition) }}
                    {{ getCompareValue(condition) }}
                  </div>
                </el-tooltip>
              </div>
              <div class="flex gap-x-2 items-center">
                <div
                  class="flex justify-center items-center p-2 hover:bg-[#e3e5e5] rounded"
                  @click.stop="onRemoveCondition(condition)"
                >
                  <i class="i-lucide-circle-minus" />
                </div>
                <div
                  :class="[
										'flex justify-center items-center p-2 rounded',
										condition.$fixed
											? 'bg-[#d7d9da]'
											: canFixed
											? 'hover:bg-[#e3e5e5]'
											: 'text-[#c1c3c4]'
									]"
                >
                  <i class="i-lucide-pin" />
                </div>
                <el-divider direction="vertical" />
                <div class="flex justify-center items-center p-2">
                  <i
                    :class="[
											'i-lucide-chevron-up transition-transform',
											activeCollapseItems.includes(condition.field)
												? 'rotate-180'
												: 'rotate-0'
										]"
                  />
                </div>
              </div>
            </div>
            <!--#endregion-->
            <el-divider class="!my-1" />
            <div v-show="activeCollapseItems.includes(condition.field)">
              <el-button
                v-click-outside="onClickOutside"
                text
                size="small"
                @click="onPickCompareSymbol($event, condition)"
              >
                {{ getCompareLabel(condition, false) }}
                <i class="i-lucide-chevron-down" />
              </el-button>
              <div
                v-if="
									!['isEmpty', 'isNotEmpty'].includes(condition.compareSymbol)
								"
                class="flex gap-x-2"
              >
                <component
                  v-if="condition.$type !== 'enum'"
                  :is="compareValueInputComponent(condition)"
                  v-model="condition.value[0]"
                />
                <div v-else class="mt-2">
                  <el-checkbox-group
                    v-if="
											['includes', 'notIncludes'].includes(
												condition.compareSymbol
											)
										"
                    v-model="condition.value"
                  >
                    <ul class="flex flex-col">
                      <el-checkbox
                        v-for="(enumValue, index) in colEnumMap[
													condition.field
												]"
                        :key="enumKey(condition.field, index)"
                        :value="enumValue"
                      >
                        <div
                          class="p-2 rounded text-[#1f1616]"
                          :style="{
														backgroundColor:
															TagColorList[index % TagColorList.length]
													}"
                        >
                          {{ enumValue }}
                        </div>
                      </el-checkbox>
                    </ul>
                  </el-checkbox-group>
                  <el-radio-group v-else v-model="condition.value[0]">
                    <ul class="flex flex-col">
                      <el-radio
                        v-for="(enumValue, index) in colEnumMap[
													condition.field
												]"
                        :key="enumKey(condition.field, index)"
                        :value="enumValue"
                      >
                        <div
                          class="p-2 rounded text-[#1f1616] leading-none"
                          :style="{
														backgroundColor:
															TagColorList[index % TagColorList.length]
													}"
                        >
                          {{ enumValue }}
                        </div>
                      </el-radio>
                    </ul>
                  </el-radio-group>
                </div>
                <el-date-picker
                  v-if="condition.value[0] === 'specificDate'"
                  v-model="condition.value[1]"
                  value-format="YYYY-MM-DD"
                  @visible-change="onSelectVisibleChange"
                />
              </div>
            </div>
          </div>
        </div>
      </el-scrollbar>
      <div
        class="mt-2 flex justify-between pr-4"
        style="--el-fill-color: #e6e7e8"
      >
        <el-button text bg @click="onPickField($event, -1)">
          <i class="i-lucide-plus text-[18px] mr-2" /> 添加筛选条件
        </el-button>
      </div>
    </div>
  </el-popover>
  <!--#endregion-->
</template>
<script setup lang="ts">
import {
  CompareSymbolLabel,
  DateConditions,
  TagColorList,
  toggle,
  typeIcon,
  tryit,
} from '../lib'
import {
  ElCheckboxGroup,
  ElInput,
  ElSelect,
  ElOption,
  ClickOutside as vClickOutside
} from 'element-plus'
import { clickPopoverOutside as vClickPopoverOutside } from '../directives'
import type { ColumnConfig, FilterCondition, FilterConfig, TCompareSymbol } from '../types'
import { nextTick, ref, type Ref, computed, h } from 'vue'
import { useToggle } from '@vueuse/core'

const props = defineProps<{
  virtualRef: any
  colEnumMap: Record<string, (string | number)[]>
  fieldPicker: InstanceType<typeof import('./FieldPicker.vue')['default']>
  symbolPicker: InstanceType<
    typeof import('./CompareSymbolPicker.vue')['default']
  >
  columns: ColumnConfig[]
}>()

const filterConfig = defineModel<FilterConfig>({ required: true })

const canFixed = computed(
	() => filterConfig.value.conditions.filter(c => c.$fixed).length < 3
)

const selectableColumns = computed(() =>
  props.columns.filter(
    col => !filterConfig.value.conditions.map(c => c.field).includes(col.field)
  )
)

// 筛选条件配置面板可见状态
const [ filterPaneVisible, toggleFilterPaneVisible ] = useToggle(false)

// 筛选条件配置面板
const filterPaneRef = ref()

// 展开的筛选配置项
const activeCollapseItems: Ref<string[]> = ref([])

const enumKey = (field: string, index: number) => `${field}-enum-${index}`
const onRemoveCondition = (condition: FilterCondition) => {
  activeCollapseItems.value = activeCollapseItems.value.filter(
    v => v !== condition.field
  )
  filterConfig.value.conditions = filterConfig.value.conditions.filter(
    c => c.field !== condition.field
  )
}

const getCompareLabel = (
  condition: FilterCondition,
  checkValue: boolean = true
) => {
  const label = CompareSymbolLabel[condition.compareSymbol]
  if ([ 'includes', 'notIncludes' ].includes(condition.compareSymbol)) {
    return label.default
  }
  if (
    (!condition.value ||
      condition.value[0] === null ||
      condition.value.length === 0) &&
    checkValue
  )
    return '未设置'
  if (condition.$type in label) return Reflect.get(label, condition.$type)
  return label.default
}

const getCompareValue = (condition: FilterCondition) => {
  switch (condition.$type) {
    case 'number':
    case 'text':
      return condition.value![0]
    case 'date':
      return condition.value![0] === 'specificDate'
        ? condition.value![1]
        : DateConditions.find(c => c.value === condition.value![0])?.label
    case 'enum':
      return condition.value!.join('、')
  }
}

// 筛选值输入组件
const compareValueInputComponent = (condition: FilterCondition): VNode => {
  switch (condition.$type) {
    case 'text':
      return h(ElInput, { placeholder: ' ' })
    case 'number':
      return h(ElInput, { type: 'number', placeholder: ' ' })
    case 'date':
      return h(
        ElSelect,
        {
          'onVisible-change': onSelectVisibleChange
        },
        {
          default: () => {
            if ([ 'equal', 'notEqual' ].includes(condition.compareSymbol))
              return DateConditions.map(opt => h(ElOption, opt))
            return DateConditions.slice(0, 4).map(opt => h(ElOption, opt))
          }
        }
      )
    default:
      return h('span')
  }
}

const currentConditionIndex = ref(-1)
const hasFilter = computed(() => filterConfig.value.conditions.length > 0)
const handlePickFilterField = (col: ColumnConfig) => {
  const picker = props.fieldPicker
  const _hasFilter = hasFilter.value
  if (currentConditionIndex.value !== -1) {
    const prevField =
      filterConfig.value.conditions[currentConditionIndex.value].field
    activeCollapseItems.value = toggle(activeCollapseItems.value, prevField)
    Object.assign(filterConfig.value.conditions[currentConditionIndex.value], {
      compareSymbol: 'equal',
      field: col.field,
      title: col.title,
      value: [],
      $type: col.type
    })
    currentConditionIndex.value = -1
    picker.toggleVisible(false)

    activeCollapseItems.value = toggle(activeCollapseItems.value, col.field)
    return
  }
  filterConfig.value.conditions.push({
    field: col.field,
    title: col.title,
    compareSymbol: 'equal',
    value: [],
    $type: col.type,
    $fixed: false
  })
  activeCollapseItems.value.push(col.field)
  !filterConfig.value.logicSymbol && (filterConfig.value.logicSymbol = 'AND')

  nextTick()
    .then(() => {
      picker.toggleVisible(false)
    })
    .then(() => {
      if (_hasFilter) return
      toggleFilterPaneVisible(true)
    })
}

const onPickField = async (e: MouseEvent, index: number) => {
  const picker = props.fieldPicker
  picker.setVirtualRef(e.target as HTMLElement | null)
  picker.setSelectableColumns(selectableColumns.value)
  picker.toggleVisible(true)
  currentConditionIndex.value = index
  const [ cancel, field ] = await tryit<never[], Promise<ColumnConfig>>(
    picker.pick
  )()
  if (cancel) {
    currentConditionIndex.value = -1
    return
  }
  handlePickFilterField(field)
}

const onPickCompareSymbol = async (
  e: MouseEvent,
  condition: FilterCondition
) => {
  const picker = props.symbolPicker
  picker.setVirtualRef(e.target as HTMLElement | null)
  picker.setCompareDataType(condition.$type)
  picker.setModelValue(condition.compareSymbol)
  picker.toggleVisible(true)
  const [ cancel, compareSymbol ] = await tryit<
    never[],
    Promise<TCompareSymbol>
  >(picker.pick)()
  if (cancel) {
    return
  }
  condition.compareSymbol = compareSymbol
  condition.value = []
}

const [ ignoreClickOutside, toggleIgnoreClickOutside ] = useToggle(false)
const onSelectVisibleChange = (visible: boolean) => {
  toggleIgnoreClickOutside(visible)
}

const onClickPopoverOutside = () => {
  if (ignoreClickOutside.value) return
  toggleFilterPaneVisible(false)
}

const onClickOutside = () => {
  props.fieldPicker.toggleVisible(false)
  props.symbolPicker.toggleVisible(false)
}

defineExpose({
  toggleVisible: toggleFilterPaneVisible,
  handlePickFilterField
})
</script>
